#include "chitu.h"
#include "chitu_base.h"
#include "chitu_math.h"

static char *ptr_name = "__ptr__";

#define getptr_fromunit(vm, unit_p) ((void *)obj_toptr(vm, obj_indexunit(vm, *unit_p, obj_newstr(vm, ptr_name))))

// #数组的创建和操作
int ELSAPI_chitu_chitu_array_create(els_VmObj* vm) // chitu_array_create(u:unit, len:int, init:num): ptr
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = chitu_array_create(arg_getnum(vm, 2), arg_getnum(vm, 3));

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    obj_setunit(vm, *unit, obj_newstr(vm, ptr_name), obj_newptr(vm, (void *)a));
    return 1;
}

int ELSAPI_chitu_chitu_array_free(els_VmObj* vm) // chitu_array_free(u:unit): 1
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_free(a);
    obj_setunit(vm, *unit, obj_newstr(vm, ptr_name), obj_newnull(vm));
    return 1;
}

int ELSAPI_chitu_chitu_array_dataptr(els_VmObj* vm) // chitu_array_dataptr(u:unit): ptr
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    arg_returnptr(vm, (void *)chitu_array_dataptr(a));
    return 1;
}

int ELSAPI_chitu_chitu_array_clear(els_VmObj* vm) // chitu_array_clear(u:unit): null
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_clear(a);
    return 1;
}

int ELSAPI_chitu_chitu_array_set(els_VmObj* vm) // chitu_array_set(u:unit, n:num): num
{
    LosuObj *unit = arg_get(vm, 1);
    double n = arg_getnum(vm, 2);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_set(a, n);
    arg_returnnum(vm, n);
    return 1;
}

int ELSAPI_chitu_chitu_array_cat(els_VmObj* vm) // chitu_array_cat(a:unit, b:unit): int
{
    LosuObj *unit1 = arg_get(vm, 1);
    LosuObj *unit2 = arg_get(vm, 2);
    chitu_array *a = getptr_fromunit(vm, unit1);
    chitu_array *b = getptr_fromunit(vm, unit2);
    chitu_array *newa;

    if (a == NULL || b == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    newa = chitu_array_cat(a, b);
    chitu_array_free(a);
    obj_setunit(vm, *unit1, obj_newstr(vm, ptr_name), obj_newptr(vm, (void *)newa));
    arg_return(vm, *unit1);
    return 1;
}

int ELSAPI_chitu_chitu_array_slice(els_VmObj* vm) // chitu_array_slice(u:unit, start:int, end:int): unit
{
    chitu_array *a, *newa;
    LosuObj *unit = arg_get(vm, 1);
    double start = arg_getnum(vm, 2);
    double end = arg_getnum(vm, 3);

    a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    newa = chitu_array_slice(a, start, end);
    if (newa != NULL)
        arg_returnptr(vm, (void *)newa);
    else
        arg_returnnull(vm);
    
    return 1;
}

int ELSAPI_chitu_chitu_array_length(els_VmObj* vm) // chitu_array_length(u:unit): int
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    arg_returnnum(vm, chitu_array_length(a));
    return 1;
}

int ELSAPI_chitu_chitu_array_copy(els_VmObj* vm) // chitu_array_copy(u:unit): unit
{
    LosuObj *unit1 = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit1);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array *newa = chitu_array_copy(a);
    arg_returnptr(vm, (void *)newa);
    return 1;
}

int ELSAPI_chitu_chitu_array_get(els_VmObj* vm)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    uint64_t index = arg_getnum(vm, 2);
    double data;

    if (chitu_array_get(a, index, &data))
        arg_returnnum(vm, data);
    else
        arg_returnnull(vm);

    return 1;
}

int ELSAPI_chitu_chitu_array_insert(els_VmObj* vm)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    uint64_t index = arg_getnum(vm, 2);
    double data = arg_getnum(vm, 3);

    if (chitu_array_insert(a, index, data))
        arg_returnnum(vm, data);
    else
        arg_returnnull(vm);

    return 1;
}

int ELSAPI_chitu_chitu_array_print(els_VmObj* vm)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_print(a);
    return 1;
}

// #自定义运算
int ELSAPI_chitu_chitu_array_cfunct(els_VmObj* vm) // chitu_array_cfunct(u:unit, f:ptr): unit
{
    LosuObj *unit = arg_get(vm, 1);
    double (*cfunct)(double) = (void *)arg_getptr(vm, 2);
    cfunct = (void *)arg_getptr(vm, 2);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_cfunct(a, cfunct);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_lsfunct(els_VmObj* vm) // chitu_array_lsfunct(u:unit, f:ptr): unit
{
    return 1;
}

// #四则运算
int ELSAPI_chitu_chitu_array_plus(els_VmObj* vm) // chitu_array_plus(u:unit, n:num): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }
    
    if (arg_gettype(vm, 2) == ELS_API_TYPE_NUMBER)
    {    
        double n = arg_getnum(vm, 2);
        chitu_array_plus(a, n);
    }
    else if (arg_gettype(vm, 2) == ELS_API_TYPE_UNIT)
    {
        LosuObj *unit2 = arg_get(vm, 2);
        LosuObj *ptr_obj = obj_indexunit(vm, *unit2, obj_newstr(vm, ptr_name));
        chitu_array *b;

        if (obj_type(vm, ptr_obj) == ELS_API_TYPE_PTR)
        {
            b = (void *)obj_toptr(vm, ptr_obj);
            chitu_array_plusa(a, b);
        }
    }
    
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_minus(els_VmObj* vm) // chitu_array_minus(u:unit, n:num): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }
    
    if (arg_gettype(vm, 2) == ELS_API_TYPE_NUMBER)
    {    
        double n = arg_getnum(vm, 2);
        chitu_array_minus(a, n);
    }
    else if (arg_gettype(vm, 2) == ELS_API_TYPE_UNIT)
    {
        LosuObj *unit2 = arg_get(vm, 2);
        LosuObj *ptr_obj = obj_indexunit(vm, *unit2, obj_newstr(vm, ptr_name));
        chitu_array *b;

        if (obj_type(vm, ptr_obj) == ELS_API_TYPE_PTR)
        {
            b = (void *)obj_toptr(vm, ptr_obj);
            chitu_array_minusa(a, b);
        }
    }
    
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_mutiplys(els_VmObj* vm) // chitu_array_mutiplys(u:unit, n:num): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }
    
    if (arg_gettype(vm, 2) == ELS_API_TYPE_NUMBER)
    {    
        double n = arg_getnum(vm, 2);
        chitu_array_mutiplys(a, n);
    }
    else if (arg_gettype(vm, 2) == ELS_API_TYPE_UNIT)
    {
        LosuObj *unit2 = arg_get(vm, 2);
        LosuObj *ptr_obj = obj_indexunit(vm, *unit2, obj_newstr(vm, ptr_name));
        chitu_array *b;

        if (obj_type(vm, ptr_obj) == ELS_API_TYPE_PTR)
        {
            b = (void *)obj_toptr(vm, ptr_obj);
            chitu_array_mutiplysa(a, b);
        }
    }
    
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_divide(els_VmObj* vm) // chitu_array_divide(u:unit. n:num): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    if (arg_gettype(vm, 2) == ELS_API_TYPE_NUMBER)
    {    
        double n = arg_getnum(vm, 2);
        chitu_array_divide(a, n);
    }
    else if (arg_gettype(vm, 2) == ELS_API_TYPE_UNIT)
    {
        LosuObj *unit2 = arg_get(vm, 2);
        LosuObj *ptr_obj = obj_indexunit(vm, *unit2, obj_newstr(vm, ptr_name));
        chitu_array *b;

        if (obj_type(vm, ptr_obj) == ELS_API_TYPE_PTR)
        {
            b = (void *)obj_toptr(vm, ptr_obj);
            chitu_array_dividea(a, b);
        }
    }

    arg_return(vm, *unit);
    return 1;
}

// #取模
int ELSAPI_chitu_chitu_array_mod(els_VmObj* vm) // chitu_array_mod(u:unit, n:num): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    if (arg_gettype(vm, 2) == ELS_API_TYPE_NUMBER)
    {    
        double n = arg_getnum(vm, 2);
        chitu_array_mod(a, n);
    }
    else if (arg_gettype(vm, 2) == ELS_API_TYPE_UNIT)
    {
        LosuObj *unit2 = arg_get(vm, 2);
        LosuObj *ptr_obj = obj_indexunit(vm, *unit2, obj_newstr(vm, ptr_name));
        chitu_array *b;

        if (obj_type(vm, ptr_obj) == ELS_API_TYPE_PTR)
        {
            b = (void *)obj_toptr(vm, ptr_obj);
            chitu_array_moda(a, b);
        }
    }

    arg_return(vm, *unit);
    return 1;
}

// #指数运算
int ELSAPI_chitu_chitu_array_squre(els_VmObj* vm) // chitu_array_squre(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_squre(a);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_sqrt(els_VmObj* vm) // chitu_array_sqrt(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_sqrt(a);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_cube(els_VmObj* vm) // chitu_array_cube(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_cube(a);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_cbrt(els_VmObj* vm) // chitu_array_cbrt(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_cbrt(a);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_pow(els_VmObj* vm) // chitu_array_pow(u:unit, n:num): unit
{
    LosuObj *unit = arg_get(vm, 1);
    double n = arg_getnum(vm, 2);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_pow(a, n);
    arg_return(vm, *unit);
    return 1;
}

// #杂项
int ELSAPI_chitu_chitu_array_sum(els_VmObj* vm) // chitu_array_sum(u:unit)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }
    
    arg_returnnum(vm, chitu_array_sum(a));
    return 1;
}

int ELSAPI_chitu_chitu_array_even(els_VmObj* vm) // chitu_array_even(u:unit)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }
    
    arg_returnnum(vm, chitu_array_even(a));
    return 1;
}

int ELSAPI_chitu_chitu_array_variance(els_VmObj* vm) // chitu_array_variance(u:unit)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }
    
    arg_returnnum(vm, chitu_array_variance(a));
    return 1;
}

int ELSAPI_chitu_chitu_array_standard_deviation(els_VmObj* vm) // chitu_array_standard_deviation(u:unit)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    arg_returnnum(vm, chitu_array_standard_deviation(a));
    return 1;
}

int ELSAPI_chitu_chitu_array_max(els_VmObj* vm) // chitu_array_max(u:unit): number
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);
    double result;

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_max(a, &result);
    arg_returnnum(vm, result);
    return 1;
}

int ELSAPI_chitu_chitu_array_min(els_VmObj* vm) // chitu_array_min(u:unit): number
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);
    double result;

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_min(a, &result);
    arg_returnnum(vm, result);
    return 1;
}

// #排序
int ELSAPI_chitu_chitu_array_sort(els_VmObj* vm) // chitu_array_sort(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_sort(a);

    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_reverse(els_VmObj* vm) // chitu_array_reverse(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_reverse(a);

    arg_return(vm, *unit);
    return 1;
}

// #随机函数
int ELSAPI_chitu_chitu_array_srand(els_VmObj* vm) // chitu_array_srand(u:unit, seed:number): unit
{
    uint64_t seed = arg_getnum(vm, 2);
    chitu_srand(seed);
    return 1;
}

int ELSAPI_chitu_chitu_array_random(els_VmObj* vm) // chitu_array_random(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_random(a);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_randint(els_VmObj* vm) // chitu_array_randint(u:unit, start:int, end:int): unit
{
    LosuObj *unit = arg_get(vm, 1);
    uint64_t start = arg_getnum(vm, 2);
    uint64_t end = arg_getnum(vm, 3);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_randint(a, start, end);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_randfloat(els_VmObj* vm) // chitu_array_randfloat(u:unit, start:num, end:num): unit
{
    LosuObj *unit = arg_get(vm, 1);
    double start = arg_getnum(vm, 2);
    double end = arg_getnum(vm, 3);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    chitu_array_randfloat(a, start, end);
    arg_return(vm, *unit);
    return 1;
}

int ELSAPI_chitu_chitu_array_choice(els_VmObj* vm) // chitu_array_choice(u:unit): unit
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    arg_returnnum(vm, chitu_array_choice(a));
    return 1;
}

int ELSAPI_chitu_chitu_array_tounit(els_VmObj* vm)
{
    LosuObj *unit = arg_get(vm, 1);
    chitu_array *a = getptr_fromunit(vm, unit);

    if (a == NULL)
    {
        arg_returnnull(vm);
        return 1;
    }

    LosuObj output = obj_newunit(vm);
    double val;

    for (uint64_t i = 0; i < chitu_array_length(a); i++)
    {
        chitu_array_get(a, i, &val);
        obj_setunit(vm, output, obj_newnum(vm, i + 1), obj_newnum(vm, val));
    }
    
    arg_return(vm, output);
    return 1;
}

int ELSAPI_chitu_chitu_array_fromunit(els_VmObj* vm)
{
    return 1;
}
